﻿using EFCore.EntityFrameworkExtensions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading.Tasks;

namespace System.Linq
{
    /// <summary>
    /// 表达式扩展
    /// </summary>
    /// <typeparam name="T">泛型</typeparam>
    public static class ExpressionExtension
    {
        /// <summary>
        /// 表达式动态拼接查询
        /// </summary>
        public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source, IList<QueryEntity> entities)
        {
            if (entities.Count == 0)
            {
                return source;
            }
            ParameterExpression param = Expression.Parameter(typeof(TSource));
            var expression = CreateExpressionDelegate(param,entities);            
            var lambda_expression = Expression.Lambda<Func<TSource, bool>>(expression, param);
            return source.Where(lambda_expression);
        }

        /// <summary>
        /// 创建Expression
        /// </summary>
        /// <param name="param"></param>
        /// <param name="entities"></param>
        /// <param name="fromIndex"></param>
        /// <returns></returns>
        public static Expression CreateExpressionDelegate(ParameterExpression param, IList<QueryEntity> entities,int fromIndex=0)
        {            
            var first_entitie = entities[fromIndex];
            var left_expression = CreateExpressionDelegate(param, first_entitie);
            for (var i = fromIndex + 1; i < entities.Count; i++)
            {
                var entitiy = entities[i];
                Expression right_expression = null;
                if ("OR".Equals(entitiy.LogicalOperator, StringComparison.OrdinalIgnoreCase))
                {
                    right_expression = CreateExpressionDelegate(param, entities, i);
                    left_expression = AndOrExpression(entitiy.LogicalOperator, left_expression, right_expression);
                    break;
                }
                else
                {
                    right_expression = CreateExpressionDelegate(param, entitiy);
                    left_expression = AndOrExpression(entitiy.LogicalOperator, left_expression, right_expression);
                }               
            }   
            return left_expression;
        }

        /// <summary>
        /// 创建 Expression
        /// </summary>
        private static Expression CreateExpressionDelegate(ParameterExpression param, QueryEntity entity)
        {
            Expression key = param;
            var entityKey = entity.Key.Trim();
            // 包含'.'，说明是父表的字段
            if (entityKey.Contains('.'))
            {
                var tableNameAndField = entityKey.Split('.');
                key = Expression.Property(key, tableNameAndField[0].ToString());
                key = Expression.Property(key, tableNameAndField[1].ToString());
            }
            else
            {
                key = Expression.Property(key, entityKey);
            }

            Expression value = Expression.Constant(ParseType(param.Type, entity));
            Expression body = CreateExpression(key, value, entity.Operator);

            if (entity.ChildQueryEntitys?.Count > 0)
            {
                var child_expression = CreateExpressionDelegate(param, entity.ChildQueryEntitys);
                body = AndOrExpression(entity.ChildQueryEntitys[0].LogicalOperator, body, child_expression);
            }
            return body;
        }

        private static Expression AndOrExpression(string LogicalOperator, Expression left_expression, Expression child_expression)
        { 
            if ("OR".Equals(LogicalOperator, StringComparison.OrdinalIgnoreCase))
            {
                left_expression = Expression.OrElse(left_expression, child_expression);
            }
            else
            {
                left_expression = Expression.AndAlso(left_expression, child_expression);
            }
            return left_expression;
        }


        /// <summary>
        /// 属性类型转换
        /// </summary>
        /// <param name="entity">查询实体</param>
        /// <returns></returns>
        private static object ParseType(Type TSourceType, QueryEntity entity)
        {
            try
            {
                PropertyInfo property;
                // 包含'.'，说明是子类的字段
                if (entity.Key.Contains('.'))
                {
                    var tableNameAndField = entity.Key.Split('.');

                    property = TSourceType.GetProperty(tableNameAndField[0], BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
                    property = property.PropertyType.GetProperty(tableNameAndField[1], BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
                }
                else
                {
                    property = TSourceType.GetProperty(entity.Key, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
                }

                return Convert.ChangeType(entity.Value, property.PropertyType);
            }
            catch (Exception)
            {
                throw new ArgumentException("字段类型转换失败：字段名错误或值类型不正确");
            }
        }

        /// <summary>
        /// 创建 Expression
        /// </summary>
        private static Expression CreateExpression(Expression left, Expression value, string entityOperator)
        {
            if (!Enum.TryParse(entityOperator, true, out OperatorEnum operatorEnum))
            {
                throw new ArgumentException("操作方法不存在,请检查operator的值");
            }

            return operatorEnum switch
            {
                OperatorEnum.Equals => Expression.Equal(left, value),
                OperatorEnum.NotEqual => Expression.NotEqual(left, value),
                OperatorEnum.Contains => Expression.Call(left, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), value),
                OperatorEnum.StartsWith => Expression.Call(left, typeof(string).GetMethod("StartsWith", new Type[] { typeof(string) }), value),
                OperatorEnum.EndsWith => Expression.Call(left, typeof(string).GetMethod("EndsWith", new Type[] { typeof(string) }), value),
                OperatorEnum.Greater => Expression.GreaterThan(left, value),
                OperatorEnum.GreaterEqual => Expression.GreaterThanOrEqual(left, value),
                OperatorEnum.Less => Expression.LessThan(left, value),
                OperatorEnum.LessEqual => Expression.LessThanOrEqual(left, value),
                _ => Expression.Equal(left, value),
            };
        }
    }
}
